package org.litu.app.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.litu.app.entity.vo.CommonData;
import org.litu.app.entity.vo.LoginUserInfo;
import org.litu.app.entity.vo.LoginUserMsg;
import org.litu.app.service.baseimpl.ILoginExService;
import org.litu.base.log.IBaseLogService;
import org.litu.base.log.LtLog;
import org.litu.base.log.LtLogOperation;
import org.litu.base.log.LtLogOperationEnum;
import org.litu.core.base.ApiRes;
import org.litu.core.base.BaseController;
import org.litu.core.enums.ResultEnum;
import org.litu.core.exception.LtServerException;
import org.litu.core.login.TokenCheck;
import org.litu.core.login.TokenUtil;
import org.litu.util.net.NetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用户登录/登出
 *
 * @author yueye
 */
@LtLog(module = "用户登陆/登出模块")
@Api(value = "用户登陆/退出", tags = {"用户登陆/退出"}, protocols = "http,https")
@RestController
public class LoginController extends BaseController {

    @Autowired
    ILoginExService loginService;
    @Autowired
    IBaseLogService optLogService;


    @Autowired
    TokenUtil tokenUtil;

    /**
     * 用户登录使用， 通过用户名密码登录，并获取到单点登录的token和用户信息。
     * 这里不进行日志的输出，直接写在了代码中，因为登陆起初是无用户信息的，日志通过切片记录不合理。
     *
     * @param account
     * @param password
     * @return
     */
    @PostMapping(value = "/login")
    @ApiOperation(value = "登录接口", notes = "用户请求，根据用户名密码获取token和用户信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "systemCode", value = "系统编号", required = true),
            @ApiImplicitParam(name = "account", value = "用户账号", required = true),
            @ApiImplicitParam(name = "password", value = "用户密码，md5加密", required = true)
    })
    @TokenCheck(check = false)
    public ApiRes<LoginUserMsg> login(String systemCode, String account, String password) {
        if (StringUtils.isAnyBlank(account, password, systemCode)) {
            return ApiRes.error(ResultEnum.ParamError, "系统编号、用户名或密码不能为空!");
        }
        if (!loginService.checkSystemCode(systemCode)) {
            return ApiRes.error(ResultEnum.InvalidRequest, String.format("系统【%s】不受权限系统管理，请确认!", systemCode));
        }

        ApiRes result = null;
        try {
            // 获取验证登录信息以及获取用户信息
            LoginUserInfo user = loginService.checkLogin(systemCode, account, password);
            if (user == null) {
                return ApiRes.error(ResultEnum.UserPwdError);
            }
            LoginUserMsg userMsg = loginService.getLoginMsg(user, systemCode);

            String ip = NetUtil.getIp(request);
            String token = tokenUtil.newToken(ip, systemCode);
            tokenUtil.setToken(token, user);

            userMsg.setToken(token);

            // 记录登录日志(异步)
            optLogService.setLogs("登陆注销", "用户登陆", "用户登陆", ip, user.getId(), systemCode).addOptLogsRunnable();

            result = ApiRes.ok(userMsg);
        } catch (LtServerException se) {
            result = ApiRes.error(se.getErrorMsg(), se.getMessage());
        } catch (Exception e) {
            result = ApiRes.error(ResultEnum.UserPwdError, "用户名或密码错误!");
        }
        return result;
    }

    /**
     * 使用token信息调用该接口，获取该token对应的菜单等数据信息。
     * 如果该token失效，则直接返回失效信息
     * <p>
     * 该接口主要是为app等相关长期登陆用户保留的一个口。
     *
     * @return
     */
    @PostMapping(value = "/loginToken")
    @ApiOperation(value = "获取登陆用户，该系统的信息", notes = "使用token信息调用该接口，获取该token对应的菜单等数据信息。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "systemCode", value = "系统编号", paramType = "query", required = true)
    })
    @TokenCheck
    public ApiRes<LoginUserMsg> loginToken(@RequestParam(required = true) String systemCode) {
        if (StringUtils.isBlank(systemCode)) {
            return ApiRes.error(ResultEnum.ParamError, "系统编号不能为空!");
        }

        if (!loginService.checkSystemCode(systemCode)) {
            return ApiRes.error(ResultEnum.InvalidRequest, String.format("系统【%s】不受权限系统管理，请确认!", systemCode));
        }

        try {
            // 认证成功，直接返回给前台用户的信息。
            LoginUserInfo user = nowUser();
            if (null == user) {
                return ApiRes.error(ResultEnum.TokenTimeout, "当前登陆信息已经失效，请重新登陆");
            }

            LoginUserMsg userMsg = loginService.getLoginMsg(user, systemCode);
            return ApiRes.ok(userMsg);
        } catch (LtServerException lex) {
            return ApiRes.error(lex.getErrorMsg(), lex.getMessage());
        } catch (Exception e) {
            return ApiRes.error(ResultEnum.ServerError, "登录失败！");
        }
    }

    /**
     * 用户登录成功后需要获取的系统配置和静态数据
     * 根据登录用户的token信息，获取对应的菜单数据、字典数据、配置数据等
     * TODO 系统配置还需要再增加下,配置项加一个是否Web端使用之类的内容
     *
     * @return 系统配置和静态数据
     */
    @PostMapping("/data")
    @ApiOperation(value = "获取系统数据", notes = "系统使用到的基础数据：菜单、字典，系统配置")
    @TokenCheck
    public ApiRes data(@RequestParam(required = true) String systemCode) {
        try {
            LoginUserInfo user = nowUser();
            CommonData data = loginService.getCommonData(systemCode, user.getId());

            return ApiRes.ok(data);
        } catch (LtServerException lex) {
            return ApiRes.error(lex.getErrorMsg(), lex.getMessage());
        } catch (Exception e) {
            return ApiRes.error(ResultEnum.ServerError, "获取数据系统数据出现错误！");
        }
    }

    /**
     * 登陆用户退出
     *
     * @return 成功则显示注销成功
     */
    @PostMapping("/logout")
    @LtLogOperation(operation = LtLogOperationEnum.LOGOUT)
    @ApiOperation(value = "用户退出", notes = "用户直接退出登陆")
    @TokenCheck
    public ApiRes logout() {
        String token = token();
        tokenUtil.delToken(token);

        return ApiRes.ok("注销成功！");
    }
}
